package com.example.system.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.starter.mybatis.configure.MybatisProperties;
import com.example.starter.util.ExceptionUtil;
import com.example.starter.webmvc.util.WebUtil;
import com.example.system.convert.AuthorityConvert;
import com.example.system.dto.AuthorityDto;
import com.example.system.entity.Authority;
import com.example.system.entity.Role;
import com.example.system.mapper.AuthorityMapper;
import com.example.system.service.IAuthorityService;
import com.example.system.service.IRoleService;
import com.example.system.vo.Menu;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 王令
 * @since 2022-08-05
 */
@Service
@RequiredArgsConstructor
public class AuthorityServiceImpl extends ServiceImpl<AuthorityMapper, Authority> implements IAuthorityService {

    private final IRoleService roleService;
    private final MybatisProperties properties;

    @Override
    public void create(AuthorityDto authorityDto) {
        Authority authority = AuthorityConvert.INSTANCE.toAuthority(authorityDto);
        assertUniqueRecord(authority);
        this.save(authority);
    }

    @Override
    public void update(AuthorityDto authorityDto) {
        Authority authority = AuthorityConvert.INSTANCE.toAuthority(authorityDto);
        if (ObjectUtil.equal(authority.getId(), authority.getParentId())) {
            throw ExceptionUtil.wrap("上级项不能是自己本身");
        }
        assertUniqueRecord(authority);
        this.updateById(authority);
    }

    @Override
    public void deleteById(Long id) {
        assertSafeDelete(id);
        this.removeById(id);
    }

    @Override
    public List<Authority> listTreeMenuByRolesId(List<Long> rolesId) {
        if (properties.isMaster(WebUtil.getTenantId())) {
            return baseMapper.listMenuByRolesIdWithMaster(rolesId);
        }
        return baseMapper.listMenuByRolesId(rolesId, WebUtil.getTenantId());
    }

    @Override
    public List<Authority> listPermissionByRolesId(List<Long> rolesId) {
        if (ObjectUtil.isEmpty(rolesId)) {
            return ListUtil.empty();
        }
        if (properties.isMaster(WebUtil.getTenantId())) {
            return baseMapper.listPermissionByRolesIdWithMaster(rolesId);
        }
        return baseMapper.listPermissionByRolesId(rolesId, WebUtil.getTenantId());
    }

    @Override
    public List<Tree<Long>> listWithTree() {
        List<Authority> allAuthorities;
        if (properties.isMaster(WebUtil.getTenantId())) {
            allAuthorities = baseMapper.listAllByMaster();
        } else {
             allAuthorities = baseMapper.listAllAuthority(WebUtil.getTenantId());
        }
        List<TreeNode<Long>> treeNodes = allAuthorities.stream()
                        .map(getNodeFunction()).collect(
                                Collectors.toList());
        return TreeUtil.build(treeNodes, 0L);
    }

    @Override
    public List<Tree<Long>> listWithTreeMenu(List<String> rolesCode) {
        if (ObjectUtil.isEmpty(rolesCode)) {
            return ListUtil.empty();
        }
        List<TreeNode<Long>> treeNodes =
                listTreeMenuByRolesId(roleService.list(Wrappers.<Role>lambdaQuery()
                        .in(Role::getCode, rolesCode)).stream().map(Role::getId).collect(Collectors.toList()))
                        .stream().map(AuthorityConvert::toMenu).map(getMenuNodeFunction()).collect(Collectors.toList());
        return TreeUtil.build(treeNodes, 0L);
    }

    @NotNull
    private Function<Authority, TreeNode<Long>> getNodeFunction() {
        return authority -> {
            TreeNode<Long> node = new TreeNode<>();
            node.setId(authority.getId());
            node.setName(authority.getName());
            node.setWeight(authority.getOrder());
            node.setParentId(authority.getParentId());
            Map<String, Object> extra = new HashMap<>();
            extra.put("title", authority.getTitle());
            extra.put("path", authority.getPath());
            extra.put("type", authority.getType());
            extra.put("component", authority.getComponent());
            extra.put("redirect", authority.getRedirect());
            extra.put("order", authority.getOrder());
            extra.put("meta", authority.getMeta());
            node.setExtra(extra);
            return node;
        };
    }

    @NotNull
    private Function<Menu, TreeNode<Long>> getMenuNodeFunction() {
        return menu -> {
            TreeNode<Long> node = new TreeNode<>();
            node.setId(menu.getId());
            node.setWeight(menu.getOrder());
            node.setName(menu.getName());
            node.setParentId(menu.getParentId());
            Map<String, Object> extra = new HashMap<>();
            extra.put("path", menu.getPath());
            extra.put("component", menu.getComponent());
            extra.put("redirect", menu.getRedirect());
            extra.put("order", menu.getOrder());
            extra.put("meta", menu.getMeta());
            node.setExtra(extra);
            return node;
        };
    }

    private void assertUniqueRecord(Authority authority) {
        LambdaQueryWrapper<Authority> wrapper = Wrappers.<Authority>lambdaQuery()
                .eq(Authority::getName, authority.getName())
                .ne(authority.getId() != null, Authority::getId, authority.getId());
        Authority one = baseMapper.selectOne(wrapper);
        if (one != null) {
            throw ExceptionUtil.wrap("资源编码已存在");
        }
    }

    private void assertSafeDelete(Long id) {
        Integer count = baseMapper.selectCount(Wrappers.<Authority>lambdaQuery().eq(Authority::getParentId, id));
        if (count != null && count > 0) {
            throw ExceptionUtil.wrap("存在下级项，不能删除");
        }
    }

}
